﻿using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Windows;
using ChatClient.Models;
using DotnetUtil.Extensions;
using DotnetUtil.Services;

namespace ChatClient.Services
{
    public class SessionService : ISessionService
    {
        public event Action<Participant> NewTextMessage = (_) => { };
        public event Action<Participant> NewImageMessage = (_) => { };
        public event Action<bool> IsConnectedChanged = (_) => { };

        public string? UserName { get; set; }
        public string? ProfilePic { get; set; }

        public bool IsConnected
        {
            get => _isConnected;
            private set
            {
                _isConnected = value;
                IsConnectedChanged.Invoke(value);
            }
        }

        public bool IsLoggedIn { get; set; }

        public ObservableCollection<Participant> Participants { get; }

        private volatile bool _isWorking;
        private bool _isConnected;

        private readonly ILogService _logService;
        private readonly IChatService _chatService;

        public SessionService(
            ILogService logService,
            IChatService chatService
        )
        {
            _logService = logService;
            _chatService = chatService;

            Participants = new ObservableCollection<Participant>();

            chatService.ParticipantConnected += OnParticipantConnected;
            chatService.ParticipantDisconnected += OnParticipantDisconnected;
            chatService.ParticipantLoggedIn += OnParticipantLoggedIn;
            chatService.ParticipantLoggedOut += OnParticipantLoggedOut;
            chatService.ConnectionReconnecting += OnConnectionReconnecting;
            chatService.ConnectionReconnected += OnConnectionReconnected;
            chatService.ConnectionClosed += OnConnectionClosed;
            chatService.NewTextMessage += OnNewTextMessage;
            chatService.NewImageMessage += OnNewImageMessage;
            chatService.ParticipantTyping += OnParticipantTyping;

            _isWorking = true;
        }

        public void Startup()
        {
            if (!_isWorking)
            {
                return;
            }

            Task.Run(async () =>
            {
                try
                {
                    await _chatService.ConnectAsync();
                    IsConnected = true;
                }
                catch (Exception e)
                {
                    _logService.Error($"连接服务器失败：{e.GetFullMessage()}");
                    Startup();
                }
            });
        }

        public void Shutdown()
        {
            _isWorking = false;
            _chatService.Disconnect();
        }

        public byte[] GetAvatar() =>
            string.IsNullOrEmpty(ProfilePic)
                ? Array.Empty<byte>()
                : File.ReadAllBytes(ProfilePic);

        private void OnParticipantLoggedIn(User user)
        {
            var ptp = Participants.FirstOrDefault(p => string.Equals(p.Name, user.Name));
            if (IsLoggedIn && ptp == null)
            {
                Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
                {
                    Participants.Add(new Participant
                    {
                        Name = user.Name,
                        Photo = user.Photo
                    });
                }));
            }
        }

        private void OnParticipantLoggedOut(string name)
        {
            OnParticipantDisconnected(name);
        }

        private void OnParticipantDisconnected(string name)
        {
            var person = Participants.FirstOrDefault((p) => string.Equals(p.Name, name));
            if (person != null)
            {
                Application.Current?.Dispatcher.BeginInvoke(new Action(() => { person.IsLoggedIn = false; }));
            }
        }

        private void OnParticipantConnected(string name)
        {
            var person = Participants.FirstOrDefault((p) => string.Equals(p.Name, name));
            if (person != null)
            {
                Application.Current?.Dispatcher.BeginInvoke(new Action(() => { person.IsLoggedIn = true; }));
            }
        }

        private void OnConnectionReconnecting()
        {
            IsConnected = false;
            IsLoggedIn = false;
        }

        private async void OnConnectionReconnected()
        {
            IsConnected = true;
            IsLoggedIn = false;

            var pic = GetAvatar();
            if (string.IsNullOrWhiteSpace(UserName))
            {
                return;
            }

            try
            {
                await _chatService.LoginAsync(UserName, pic);
                IsLoggedIn = true;
            }
            catch (Exception e)
            {
                _logService.Error($"登录失败：{e.GetFullMessage()}");
            }
        }

        private async void OnConnectionClosed()
        {
            var connectionTask = _chatService.ConnectAsync();
            await connectionTask.ContinueWith(t =>
            {
                if (t.IsFaulted || string.IsNullOrWhiteSpace(UserName))
                {
                    return;
                }

                IsConnected = true;
                _chatService.LoginAsync(UserName, GetAvatar()).Wait();
                IsLoggedIn = true;
            });
        }

        private void OnNewTextMessage(string name, string msg, MessageType mt)
        {
            if (mt != MessageType.UniCast)
            {
                return;
            }

            var cm = new ChatMessage { Author = name, Message = msg, Time = DateTime.Now };
            var sender = Participants.FirstOrDefault((u) => string.Equals(u.Name, name));
            if (sender == null)
            {
                return;
            }

            Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
            {
                sender.Chatter.Add(cm);
                NewTextMessage.Invoke(sender);
            }));
        }

        private void OnNewImageMessage(string name, byte[] pic, MessageType mt)
        {
            if (mt != MessageType.UniCast)
            {
                return;
            }

            var imageDirectory = Path.Combine(Environment.CurrentDirectory, "Image Messages");
            if (!Directory.Exists(imageDirectory)) Directory.CreateDirectory(imageDirectory);

            var imageCount = Directory.EnumerateFiles(imageDirectory).Count() + 1;
            var imagePath = Path.Combine(imageDirectory, $"IMG_{imageCount}.jpg");

            var converter = new ImageConverter();
            using (var image = (Image)converter.ConvertFrom(pic)!)
            {
                image.Save(imagePath);
            }

            var cm = new ChatMessage { Author = name, Picture = imagePath, Time = DateTime.Now };
            var sender = Participants.FirstOrDefault(u => string.Equals(u.Name, name));
            if (sender == null)
            {
                return;
            }

            Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
            {
                sender.Chatter.Add(cm);
                NewImageMessage.Invoke(sender);
            }));
        }

        private void OnParticipantTyping(string name)
        {
            var person = Participants.FirstOrDefault((p) => string.Equals(p.Name, name));
            if (person is not { IsTyping: false })
            {
                return;
            }

            Application.Current?.Dispatcher.BeginInvoke(new Action(() => { person.IsTyping = true; }));

            Task.Run(async () =>
            {
                await Task.Delay(1500);
                Application.Current?.Dispatcher.BeginInvoke(new Action(() => { person.IsTyping = false; }));
            });
        }
    }
}
